package aole.mindfx.model;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.text.Font;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;

import aole.mindfx.FXNodeListener;

public class MindFXMethod extends MindFXNode implements
		EventHandler< ActionEvent > {
	private String								scope;
	private String								type;
	private int									pointers;
	private ObservableList< MindFXCall >		calls;
	private ObservableList< MindFXReference >	refs;
	private Button								node;
	private FXNodeListener						listener;
	private MindFXClass							parent;
	private String								purpose;
	private ObservableList< MindFXParam >		params;

	private static Font							f	= new Font( "Cambria", 16 );

	public String getCaption() {
		String s = getType();
		for( int i = 0; i < getPointers(); i++ )
			s += "*";
		s += " " + getName() + "()";
		return s;
	}

	public String getFullCaption() {
		String s = getType();
		for( int i = 0; i < getPointers(); i++ )
			s += "*";
		s += " " + parent.getName() + "::" + getName() + "()";
		return s;
	}

	public void addFXCallReference( String t, String m ) {
		MindFXReference ref = new MindFXReference( "reference", this );
		addFXCallReference( ref );
		ref.setType( t );
		ref.setMethod( m );
	}

	public void addFXCallReference( MindFXReference r ) {
		refs.add( r );
	}

	public void deleteFXReference( MindFXReference r ) {
		refs.remove( r );
	}

	public ObservableList< MindFXReference > getFXCallReferences() {
		return refs;
	}

	public ObservableList< MindFXCall > getFXCalls() {
		return calls;
	}

	public void addFXCall( MindFXCall c ) {
		calls.add( c );
	}

	public void deleteFXCall( MindFXCall c ) {
		calls.remove( c );
	}

	public ObservableList< MindFXParam > getFXParams() {
		return params;
	}

	public void addFXParam( MindFXParam p ) {
		params.add( p );
	}

	public void deleteFXParam( MindFXParam p ) {
		params.remove( p );
	}

	public String getScope() {
		return scope;
	}

	public void setScope( String scope ) {
		this.scope = scope;
	}

	public String getType() {
		return type;
	}

	public void setType( String type ) {
		this.type = type;
		node.setText( getCaption() );
	}

	public MindFXMethod( String n, FXNodeListener l, MindFXClass c ) {
		super( n );
		scope = "public";
		type = "void";
		pointers = 0;
		listener = l;
		params = FXCollections.< MindFXParam >observableArrayList();
		calls = FXCollections.< MindFXCall >observableArrayList();
		refs = FXCollections.< MindFXReference >observableArrayList();
		parent = c;

		node = new Button( name + "()" );
		node.setFont( getFont() );
		node.setStyle( getStyle() );
		node.setOnAction( this );
	}

	public int getPointers() {
		return pointers;
	}

	public void setPointers( int pointers ) {
		this.pointers = pointers;
		node.setText( getCaption() );
	}

	@Override
	public boolean setXMLAttribute( String att, String value ) {
		if( super.setXMLAttribute( att, value ) ) return true;

		switch( att ) {
		case "scope":
			setScope( value );
			return true;
		case "type":
			setType( value );
			return true;
		case "pointers":
			setPointers( Integer.parseInt( value ) );
			return true;
		}
		return false;
	}

	@Override
	public Node getFXNode() {
		return node;
	}

	@Override
	public void handle( ActionEvent event ) {
		listener.actionPerformed( this );
	}

	public static String getStyle() {
		return "-fx-base:green";
	}

	public static Font getFont() {
		return f;
	}

	@Override
	public void loadXML( org.w3c.dom.Node node ) {
		// get attributes
		NamedNodeMap atts = node.getAttributes();
		for( int i = 0; i < atts.getLength(); i++ ) {
			org.w3c.dom.Node att = atts.item( i );
			if( att.getNodeType() == org.w3c.dom.Node.ATTRIBUTE_NODE ) {
				if( !setXMLAttribute( att.getNodeName(), att.getNodeValue() ) )
					System.out.println( "Attribute not found: "
							+ att.getNodeName() + "=" + att.getNodeValue()
							+ "@" + getName() );
			}
		}
		Element e = (Element) node;
		// get purpose
		NodeList purList = e.getElementsByTagName( "purpose" );
		Element firstelement = (Element) purList.item( 0 );
		setPurpose( firstelement.getTextContent() );

		// get parameters
		NodeList nList = e.getElementsByTagName( "parameters" );
		org.w3c.dom.Node firstnode = nList.item( 0 );
		if( firstnode != null ) {
			for( org.w3c.dom.Node callnode = firstnode.getFirstChild(); callnode != null; callnode = callnode
					.getNextSibling() ) {
				if( callnode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE ) {
					String name = callnode.getNodeName();
					MindFXParam a = new MindFXParam( name, this );
					addFXParam( a );
					a.loadXML( callnode );
				}
			}
		}
		// get calls
		nList = e.getElementsByTagName( "calls" );
		firstnode = nList.item( 0 );
		if( firstnode != null ) {
			for( org.w3c.dom.Node callnode = firstnode.getFirstChild(); callnode != null; callnode = callnode
					.getNextSibling() ) {
				if( callnode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE ) {
					String name = callnode.getNodeName();
					MindFXCall a = null;
					switch( name ) {
					case "this":
						a = new MindFXCallThis( name, this );
						break;
					case "attribute":
						a = new MindFXCallAttribute( name, this,
								listener.getStructure() );
						break;
					case "parameter":
						a = new MindFXCallParam( name, this );
						break;
					}
					if( a != null ) {
						addFXCall( a );
						a.loadXML( callnode );
					} else System.out.println( "Call not found:" + name );
				}
			}
		}
		// get references
		nList = e.getElementsByTagName( "references" );
		firstnode = nList.item( 0 );
		if( firstnode != null ) {
			for( org.w3c.dom.Node callnode = firstnode.getFirstChild(); callnode != null; callnode = callnode
					.getNextSibling() ) {
				if( callnode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE ) {
					String name = callnode.getNodeName();
					MindFXReference a = new MindFXReference( name, this );
					addFXCallReference( a );
					a.loadXML( callnode );
				}
			}
		}
	}

	@Override
	public void saveXML( Document doc, Element e ) {
		e.setAttribute( "type", getType() );
		e.setAttribute( "scope", getScope() );
		e.setAttribute( "pointers", "" + pointers );
		// add purpose
		Element ep = doc.createElement( "purpose" );
		if( purpose != null ) ep.appendChild( doc.createTextNode( purpose ) );
		e.appendChild( ep );

		// add parameters
		Element a = doc.createElement( "parameters" );
		a.setAttribute( "count", "" + params.size() );
		e.appendChild( a );
		for( MindFXParam att: params ) {
			Element attr = doc.createElement( att.getName() );
			att.saveXML( doc, attr );
			a.appendChild( attr );
		}

		// add calls
		a = doc.createElement( "calls" );
		a.setAttribute( "count", "" + calls.size() );
		e.appendChild( a );
		for( MindFXCall att: calls ) {
			Element attr = doc.createElement( att.getName() );
			att.saveXML( doc, attr );
			a.appendChild( attr );
		}

		// add refs
		a = doc.createElement( "references" );
		a.setAttribute( "count", "" + refs.size() );
		e.appendChild( a );
		for( MindFXReference att: refs ) {
			Element attr = doc.createElement( att.getName() );
			att.saveXML( doc, attr );
			a.appendChild( attr );
		}
	}

	public MindFXClass getFXClass() {
		return parent;
	}

	@Override
	public Button getCodeButton() {
		String s = "";
		for( int i = 0; i < getPointers(); i++ )
			s += "*";
		Button b = new Button( s + getName() + "()" );
		b.setOnAction( this );
		return b;
	}

	public void setPurpose( String text ) {
		purpose = text;
	}

	public String getPurpose() {
		return purpose;
	}

	public MindFXParam getFXParam( String t ) {
		for( MindFXParam p: params ) {
			if( p.getName().equals( t ) ) return p;
		}
		return null;
	}

}
